package fit.conceptmap.dao.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.transaction.Transactional;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Repository;

import fit.conceptmap.beans.ConceptMapsBean;
import fit.conceptmap.beans.JsonBean;
import fit.conceptmap.beans.LinkDataArrayBean;
import fit.conceptmap.beans.NodeDataArrayBean;
import fit.conceptmap.dao.IDrawDao;
import fit.conceptmap.entities.Concept;
import fit.conceptmap.entities.Conceptmap;
import fit.conceptmap.entities.Link;
import fit.conceptmap.entities.Node;
import fit.conceptmap.entities.Relationship;
import fit.conceptmap.entities.User;
import fit.conceptmap.utils.DrawUtils;

@Repository
public class DrawDaoImpl implements IDrawDao{

	private static final Logger logger = Logger.getLogger(DrawDaoImpl.class);
	
	@PersistenceContext
	private EntityManager em;
	/**
	 * (NO USE)
	 * @return boolean add Conceptmaps success or fail
	 * Step 1: Add ConceptMaps
	 * Step 2: Add Concept
	 * Step 3: Add node
	 * Step 4: Add Relationship
	 * Step 5: Add Link
	 */
	@Override
	@Transactional
	public boolean addDraw(String username, JsonBean json) {
		boolean result = true;
		try {
			ConceptMapsBean mapsBean = DrawUtils.parseJsonToConceptMap(json);
			Random rand = new Random();
			
			// Step 1: Add ConceptMaps
			Conceptmap conceptMap = new Conceptmap();
			conceptMap.setIdConceptMaps(username + "_" + json.getNameConceptMaps());
			conceptMap.setName(json.getNameConceptMaps());
			User user = em.find(User.class, username);
			conceptMap.setUser(user);
			conceptMap.setDateCreate(new Date());
			conceptMap.setEndEdit(new Date());
			em.persist(conceptMap);
			logger.info("Add Conceptmaps successfull.");
			
			Map<Integer, Concept> concepts =  new HashMap<Integer, Concept>();
			for (NodeDataArrayBean item : mapsBean.getNodeDataArray()) {
				// Step 2: Add Concept
				Concept concept = new Concept();
				concept.setIdConcept(json.getNameConceptMaps() + "_" + item.getText());
				concept.setName(item.getText());
				em.persist(concept);
				concepts.put(item.getKey(), concept);
				
				// Step 3: Add node
				Node node = new Node();
				node.setIdNode(concept.getIdConcept() + String.valueOf(rand.nextInt()));
				node.setConcept(concept);
				node.setUser(user);
				node.setLoc(item.getLoc());
				node.setNodeKey(item.getKey());
				node.setText(item.getText());
				em.persist(node);
			}
			logger.info("Add Concept + Node successfull.");
			
			if(mapsBean.getLinkDataArray().size() > 0){
				for (LinkDataArrayBean item : mapsBean.getLinkDataArray()) {
					// Step 4: Add Relationship
					Relationship relation = new Relationship();
					Concept from = concepts.get(item.getFrom());
					Concept to = concepts.get(item.getTo());
					relation.setConceptFrom(from);
					relation.setConceptTo(to);
					relation.setConceptmap(conceptMap);
					String id = conceptMap.getIdConceptMaps() + from.getName() + to.getName() + "" + rand.nextInt();
					relation.setIdrelationship(id);
					relation.setName(from.getName() + "_" + to.getName());
					em.persist(relation);
					
					// Step 5: Add Link
					Link link = new Link();
					link.setLinkFrom(item.getFrom());
					link.setLinkTo(item.getTo());
					link.setPoint(item.getPoints());
					link.setRelationship(relation);
					link.setText("");
					link.setIdlink(relation.getIdrelationship() + Math.random());
					em.persist(link);
				}
				logger.info("Add Relationship + Link successfull.");
			}
		} catch(Exception ex) {
			result = false;
		}
		return result;
		
	}

	// Create new Concept Maps
	@Override
	@Transactional
	public boolean addConceptMaps(String username, JsonBean json) {
		boolean result = true;
		try {
			Conceptmap conceptMap = new Conceptmap();
			conceptMap.setIdConceptMaps(username + "_" + DrawUtils.random());
			User user = em.find(User.class, username);
			conceptMap.setUser(user);
			conceptMap.setDescription(json.getDescription());
			conceptMap.setName(json.getNameConceptMaps());
			conceptMap.setDateCreate(new Date());
			conceptMap.setEndEdit(new Date());
			em.persist(conceptMap);
			logger.info("Add Conceptmaps successfull.");
		} catch (Exception e) {
			result = false;
		}
		return result;
	}

	@Override
	public List<Conceptmap> getListConcepmapByUsername(String username) {
		TypedQuery<Conceptmap> query = em.createQuery("Select p From Conceptmap p Where p.user.idUser = :username ORDER BY p.dateCreate DESC",
				Conceptmap.class);
		query.setParameter("username", username);
		return query.getResultList();
	}

	@Override
	@Transactional
	public boolean addConcept(Map<Integer, Concept> concepts, JsonBean json) {
		boolean result = true;
		try {
			ConceptMapsBean mapsBean = DrawUtils.parseJsonToConceptMap(json);
			for (NodeDataArrayBean item : mapsBean.getNodeDataArray()) {
				Concept concept = new Concept();
				concept.setIdConcept(json.getNameConceptMaps() + "_" + item.getText() + "_" + DrawUtils.random());
				concept.setName(item.getText());
				em.persist(concept);
				concepts.put(item.getKey(), concept);
			}
		} catch (Exception ex) {
			System.out.println(ex.toString());
			result = false;
		}
		return result;
	}

	@Override
	@Transactional
	public boolean addNode(Map<Integer, Concept> concepts, JsonBean json, String username) {
		boolean result = true;
		try {
			ConceptMapsBean mapsBean = DrawUtils.parseJsonToConceptMap(json);
			for (NodeDataArrayBean item : mapsBean.getNodeDataArray()) {
				Node node = new Node();
				Concept concept = new Concept();
				concept = em.find(Concept.class, concepts.get(item.getKey()).getIdConcept());
				User user = em.find(User.class, username);
				node.setIdNode(concept.getIdConcept() + DrawUtils.random());
				node.setConcept(concept);
				node.setUser(user);
				node.setLoc(item.getLoc());
				node.setNodeKey(item.getKey());
				node.setText(item.getText());
				em.persist(node);
			}
		} catch (Exception ex) {
			result = false;
		}
		return result;
	}

	@Override
	@Transactional
	public boolean addRelationship(List<String> relationID, Map<Integer, Concept> concepts,
			JsonBean json, String username, String conceptmapId) {
		boolean result = true;
		try {
			Conceptmap conceptMap = em.find(Conceptmap.class, conceptmapId);
			ConceptMapsBean mapsBean = DrawUtils.parseJsonToConceptMap(json);
			for (LinkDataArrayBean item : mapsBean.getLinkDataArray()) {
				Relationship relation = new Relationship();
				Concept from = concepts.get(item.getFrom());
				Concept to = concepts.get(item.getTo());
				relation.setConceptFrom(from);
				relation.setConceptTo(to);
				relation.setConceptmap(conceptMap);
				String id = conceptMap.getIdConceptMaps() + from.getName() + to.getName() + "" + DrawUtils.random();
				relation.setIdrelationship(id);
				relation.setName(from.getName() + "_" + to.getName());
				em.persist(relation);
				relationID.add(id);
			}
		} catch (Exception ex) {
			result = false;
		}
		return result;
	}

	@Override
	@Transactional
	public boolean addLink(List<String> relationID, JsonBean json, String username) {
		boolean result = true;
		try {
			int i = 0;
			ConceptMapsBean mapsBean = DrawUtils.parseJsonToConceptMap(json);
			for (LinkDataArrayBean item : mapsBean.getLinkDataArray()) {
				Link link = new Link();
				Relationship relation = em.find(Relationship.class, relationID.get(i));
				link.setLinkFrom(item.getFrom());
				link.setLinkTo(item.getTo());
				link.setPoint(item.getPoints());
				link.setRelationship(relation);
				link.setText(item.getText());
				link.setIdlink(relation.getIdrelationship() + DrawUtils.random());
				em.persist(link);
				i++;
			}
		} catch (Exception ex) {
			result = false;
		}
		return result;
	}

	/**
	 * 
	 * @param listId of relationship
	 * @return List<Concept>
	 */
	@Override
	@Transactional
	public List<Concept> getListConceptByConceptMapId(List<String> listId) {
		List<Concept> result = new ArrayList<Concept>();
		Set<String> relationId = new HashSet<String>();
		// Get Relationship by relationship id
		for (String id : listId) { 
			TypedQuery<Relationship> query1 = em.createQuery(
					"Select p From Relationship p Where p.idrelationship = :idrelationship", 
					Relationship.class);
			query1.setParameter("idrelationship", id);
			Relationship relation = query1.getSingleResult();
			relationId.add(relation.getConceptFrom().getIdConcept());
			relationId.add(relation.getConceptTo().getIdConcept());
		}
		// Get Concept by conceptFromId or conceptToId
		for (String id : relationId) { 
			TypedQuery<Concept> query2 = em.createQuery(
					"Select p From Concept p Where p.idConcept = :idConcept", 
					Concept.class);
			query2.setParameter("idConcept", id);
			result.add(query2.getSingleResult());
		}
		return result;
	}

	/**
	 * 
	 * @param listId of concept
	 * @return List<Node>
	 */
	@Override
	public List<Node> getListNodeByListConceptId(List<String> listId) {
		List<Node> result = new ArrayList<Node>();
		String strQuery = "Select p From Node p Where p.concept.idConcept = :idConcept";
		for (String id : listId) {
			TypedQuery<Node> query = em.createQuery(strQuery, Node.class);
			query.setParameter("idConcept", id);
			Node node = query.getSingleResult();
			result.add(node);
		}
		return result;
	}

	/**
	 * 
	 * @param conceptMapsId
	 * @return List<Relationship>
	 */
	@Override
	public List<Relationship> getListRelationshipByConceptMapsId(String conceptMapsId) {
		String strQuery = "Select p From Relationship p Where p.conceptmap.idConceptMaps = :idConceptMaps";
		TypedQuery<Relationship> query = em.createQuery(strQuery, Relationship.class);
		query.setParameter("idConceptMaps", conceptMapsId);
		return query.getResultList();
	}

	/**
	 * 
	 * @param listId of relationship
	 * @return List<Link>
	 */
	@Override
	public List<Link> getListLinkByListRelationshipId(List<String> listId) {
		List<Link> result = new ArrayList<Link>();
		String strQuery = "Select p From Link p Where p.relationship.idrelationship = :idrelationship";
		for (String id : listId) {
			TypedQuery<Link> query = em.createQuery(strQuery, Link.class);
			query.setParameter("idrelationship", id);
			Link link = query.getSingleResult();
			result.add(link);
		}
		return result;
	}

}
